package cn.modoumama.common.tools;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.IOUtils;

public class ZipCompress {
	/**
	 * @param zipFileName 目的地Zip文件
	 * @param sourceFileName 源文件（带压缩的文件或文件夹）
	 * @throws Exception
	 */
	public static void zip(String zipFileName, String sourceFileName) throws Exception {
		 File zipFile = new File(zipFileName);
		System.out.println("压缩中...");
		
		OutputStream outputStream = new FileOutputStream(zipFile);
		zip(outputStream,sourceFileName);
		outputStream.close();
		System.out.println("压缩完成");

	}
	
	/**
	 * @param outputStream 输出流
	 * @param sourceFileName 源文件（带压缩的文件或文件夹）
	 * @throws Exception
	 */
	public static void zip(OutputStream outputStream, String sourceFileName) throws Exception {
		// File zipFile = new File(zipFileName);
		System.out.println("压缩中...");
		
		// 创建zip输出流
		ZipOutputStream out = new ZipOutputStream(outputStream);

		// 创建缓冲输出流
		BufferedOutputStream bos = new BufferedOutputStream(out);

		File sourceFile = new File(sourceFileName);

		// 调用函数
		compress(out, bos, sourceFile, sourceFile.getName());
		
		bos.close();
		out.close();
		System.out.println("压缩完成");

	}

	public static void zip(OutputStream outputStream, Map<String, Object> sourceFiles) throws Exception {
		zip(outputStream, sourceFiles, (file) -> {
			FileInputStream fis = null;
			if (file instanceof File) {
				fis = new FileInputStream((File)file);
			} else if (file instanceof String) {
				fis = new FileInputStream((String)file);
			}

			byte[] out = new byte[0];
			if(fis != null){
				out = IOUtils.toByteArray(fis);
				fis.close();
			}
			return out;
		});
	}

	/**
	 * @param outputStream 输出流
	 * @param sourceFiles 源文件（带压缩的文件或文件夹）
	 * @throws Exception
	 */
	public static void zip(OutputStream outputStream, Map<String, Object> sourceFiles, ReadFile readFile) throws Exception {
		// File zipFile = new File(zipFileName);
		System.out.println("压缩中...");
		
		// 创建zip输出流
		ZipOutputStream out = new ZipOutputStream(outputStream);

		// 创建缓冲输出流
		BufferedOutputStream bos = new BufferedOutputStream(out);

		// 调用函数
		compress(out, bos, sourceFiles,"",readFile);
		
		bos.close();
		out.close();
		System.out.println("压缩完成");

	}

	@SuppressWarnings("unchecked")
	private static void compress(ZipOutputStream out, BufferedOutputStream bos, Map<String, Object> sourceFiles, String base, ReadFile readFile)
			throws Exception  {
		if(sourceFiles != null && sourceFiles.size()>0) {
			for (Map.Entry<String, Object> files : sourceFiles.entrySet()) {
				Object file = files.getValue();
				if(file!= null) {
					if(file instanceof Map) {
						Map<String, Object> fileMap = (Map<String, Object>) file;
						if(fileMap.size()==0) {
							System.out.println(base+files.getKey() + "/");
							out.putNextEntry(new ZipEntry(base+files.getKey() + "/"));
						}else {
							compress(out,bos,fileMap,base+files.getKey() + "/",readFile);
						}
					}else{
						out.putNextEntry(new ZipEntry(base + files.getKey()));
						bos.write(readFile.getByte(file));
						bos.flush();
					}
				}
			}
		}
	}

	private static void compress(ZipOutputStream out, BufferedOutputStream bos, File sourceFile, String base)
			throws Exception {
		// 如果路径为目录（文件夹）
		if (sourceFile.isDirectory()) {

			// 取出文件夹中的文件（或子文件夹）
			File[] flist = sourceFile.listFiles();

			if (flist.length == 0)// 如果文件夹为空，则只需在目的地zip文件中写入一个目录进入点
			{
				System.out.println(base + "/");
				out.putNextEntry(new ZipEntry(base + "/"));
			} else// 如果文件夹不为空，则递归调用compress，文件夹中的每一个文件（或文件夹）进行压缩
			{
				for (int i = 0; i < flist.length; i++) {
					compress(out, bos, flist[i], base + "/" + flist[i].getName());
				}
			}
		} else// 如果不是目录（文件夹），即为文件，则先写入目录进入点，之后将文件写入zip文件中
		{
			out.putNextEntry(new ZipEntry(base));
			bos.write(getByteArray(sourceFile));

			bos.flush();

		}
	}

	public interface ReadFile{
		byte[] getByte(Object file) throws Exception;
	}

	public static byte[] getByteArray(File file) throws IOException {
		FileInputStream fis  = new FileInputStream(file);
		byte[] out = new byte[0];
		if(fis != null){
			out = IOUtils.toByteArray(fis);
			fis.close();
		}
		return out;
	}
	
	public static void main(String[] args) {
		try {
			zip("D:/test.zip","D:/test");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
